package org.sgmiller.quickstem;

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;


/**
 * The basic building block of the quick-stem library.  Values stored in a Stem
 * are managed transactionally, and visibility of the values to other transactions
 * is controlled by that transactions isolation level.
 * 
 * @author scott
 *
 * @param <V> 
 */
public class Stem<V> {

	public volatile Version<V> versions=null;
	
	@SuppressWarnings("unchecked")
	static final AtomicReferenceFieldUpdater<Stem,Version>
	  versionsUpdater = AtomicReferenceFieldUpdater.newUpdater
	        (Stem.class, Version.class, "versions");

	@SuppressWarnings("unchecked")
	public Stem(V initial) {
		versions=new Version(Transaction.MIN_COMMITTED, initial, null);
	}

	public Stem() {
		this(null);
	}

	/**
	 * Get the current visible value of the variable
	 */
	public V get() {
		return Transaction.current().get(this);
	}
	
	/**
	 * Set the value of the variable in the current transaction
	 */
	public void set(final V value) {
		set(Transaction.current(), value);
	}
	
	

	/**
	 * Sets the value of the variable for the given transaction
	 */
	public void set(Transaction tid, final V value) {
		//Prune uncommitted abandoned transaction data
		boolean singleton=tid==Transaction.NONE;
		if (singleton) tid=Transaction.oneShot();
		else assert(tid.state == TransactionState.OPEN);			

		Version<V> oldVersions=versions;
		Version<V> newVersions=oldVersions.extend(tid, value);

		//This looks odd, but by check whether extend changed the structure of versions, and then
		//doing an atomic swap, we avoid synchronization in the vast majority of cases
		if (oldVersions!=newVersions && !versionsUpdater.compareAndSet(this, oldVersions, newVersions)) {
			synchronized(this) {
				versions=versions.extend(tid, value);
			}
		}
	}
}
